package org.distributed.transaction.core.context;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.distributed.transaction.core.constants.TracingConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * 跟踪信息上下文 groupId app
 */
public class TracingContext {
	
	private static final Logger log = LoggerFactory.getLogger(TracingContext.class);

	private static ThreadLocal<TracingContext> tracingContextThreadLocal = new ThreadLocal<>();

	private TracingContext() {
	}

	public static TracingContext tracing() {
		TracingContext tracingContext = tracingContextThreadLocal.get();
		if (tracingContext == null) {
			tracingContext = new TracingContext();
			tracingContextThreadLocal.set(tracingContext);
			return tracingContext;
		}
		return tracingContext;
	}

	private Map<String, String> fields;

	public void beginTransactionGroup() {
		if (hasGroup()) {
			return;
		}
		Map<String, String> initFields = new HashMap<String, String>();
		initFields.put(TracingConstants.GROUP_ID, System.currentTimeMillis()+"");
		initFields.put(TracingConstants.APP_MAP, "{}");
		init(initFields);
	}

	public static void init(Map<String, String> initFields) {

		// return if null fields.
		if (Objects.isNull(initFields)) {
			log.warn("init tracingContext fail. null init fields.");
			return;
		}
		TracingContext tracingContext = tracing();
		if (Objects.isNull(tracingContext.fields)) {
			tracingContext.fields = new HashMap<>();
		}
		tracingContext.fields.putAll(initFields);
	}

	/**
	 * 判断是否有事务组
	 *
	 * @return result true:有事务组，false没有事务组
	 */
	public boolean hasGroup() {
		return Objects.nonNull(fields) && fields.containsKey(TracingConstants.GROUP_ID)
				&& StringUtils.hasText(fields.get(TracingConstants.GROUP_ID));
	}

	/**
	 * 获取事务组ID
	 *
	 * @return groupId
	 */
	public String groupId() {
		if (hasGroup()) {
			return fields.get(TracingConstants.GROUP_ID);
		}
		raiseNonGroupException();
		return "";
	}

	/**
	 * 所有的字段
	 *
	 * @return fields
	 */
	public Map<String, String> fields() {
		return this.fields;
	}

	/**
	 * 添加App
	 *
	 * @param serviceId serviceId
	 * @param address   address
	 */
	public void addApp(String serviceId, String address) {
		if (hasGroup()) {
			JSONObject map = JSON.parseObject(this.fields.get(TracingConstants.APP_MAP));
			if (map.containsKey(serviceId)) {
				return;
			}
			map.put(serviceId, address);
			this.fields.put(TracingConstants.APP_MAP, JSON.toJSONString(map));
			return;
		}
		raiseNonGroupException();
	}

	/**
	 * JSON Type App map.
	 *
	 * @return appMap
	 */
	public JSONObject appMap() {
		return JSON.parseObject(appMapString());
	}

	/**
	 * String Type App map.
	 *
	 * @return appMap
	 */
	public String appMapString() {
		if (hasGroup()) {
			String appMap = Optional.ofNullable(this.fields.get(TracingConstants.APP_MAP)).orElse("");
			log.debug("App map: {}", appMap);
			return appMap;
		}
		raiseNonGroupException();
		return "{}";
	}

	/**
	 * 销毁当前线程Tracing信息
	 */
	public void destroy() {
		if (Objects.nonNull(tracingContextThreadLocal.get())) {
			tracingContextThreadLocal.remove();
		}
	}

	private void raiseNonGroupException() {
		throw new IllegalStateException("non group id.");
	}

}
